﻿# -*- coding: utf-8 -*-
# TaoSyncGUI.py

# Copyright 2012 Ryan Schultz

# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published
# by the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.

# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.

# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>

from Tkinter import *
from ttk import *
import tkFileDialog, tkMessageBox, tkSimpleDialog

import urllib
import time
import threading
import ConfigParser
import os

import Manifest
import TaoSync

class TaoSyncGUI:
	"""GUI interface for TaoSync, an application to synchronize ArmA mods."""
	def __init__(self, master):
		
		self.version = "0.1alpha"
		
		# Initial configuration stuff
		self.known_servers = {}
		self.selected_manifest = ""
		self.current_server = None
		self.current_selection = 0
		self.is_syncing = False
		self.ui_blocked = False
		self.ts = None
		self.addon_directory = os.getcwd()
		#self.arma_exe = os.path.join(os.getcwd(), "arma2oa.exe")
		
		# Read in configs
		self.read_config()
	
		# Gui stuff follows
		#######################################################3
		self.master = master
		self.master.title("TaoSync")
		self.master.maxsize(340,290)
		self.master.minsize(340,290)
		frame = Frame(master)
		frame.grid(row=0, sticky=E+W, columnspan=2, padx=5, pady=5)
		
		menubar = Menu(master)
		
		syncmenu = Menu(menubar, tearoff = 0)
		syncmenu.add_command(label = "Add Local Manifest...", command=self.add_local_manifest)
		syncmenu.add_command(label = "Add Online Manifest...", command=self.add_net_manifest)
		syncmenu.add_command(label = "Exit", command=frame.quit)
		menubar.add_cascade(label = "Sync", menu = syncmenu)
		
		createmenu = Menu(menubar, tearoff = 0)
		createmenu.add_command(label = "Build Repository...", command=self.build_repository)
		menubar.add_cascade(label = "Build", menu = createmenu)
		
		optionsmenu = Menu(menubar, tearoff = 0)
		#optionsmenu.add_command(label = "Select ArmA 2 Executable...", command=self.say_hi)
		optionsmenu.add_command(label = "Select Addon Directory...", command=self.select_addon_directory)
		menubar.add_cascade(label = "Options", menu = optionsmenu)
		
		helpmenu = Menu(menubar, tearoff = 0)
		helpmenu.add_command(label = "About TaoSync", command=self.about)
		menubar.add_cascade(label = "Help", menu = helpmenu)
		
		master.config(menu=menubar)
		
		self.serverlabel = Label(frame, text="Favorite Servers",anchor=CENTER)
		self.serverlabel.grid(row=0,columnspan=2, stick=E+W)
		
		lbframe = Frame(frame)
		lbframe.grid(row=1, column=0, sticky=E+W, padx=5, pady=1)
		self.listscrollbar = Scrollbar(lbframe, orient=VERTICAL)
		self.listbox = Listbox(lbframe, yscrollcommand=self.listscrollbar.set,selectmode=SINGLE)
		self.listbox['width'] = 50
		self.listscrollbar.config(command=self.listbox.yview)
		self.listscrollbar.grid(row=0, column=1, sticky=N+S)
		self.listbox.grid(row=0, column=0, sticky=W+E)
		
		self.progressbar = Progressbar(frame)
		self.progressbar.grid(row=3, sticky=W+E, padx=5, pady = 5)
		self.proglabel = Label(frame, text="Ready",anchor=CENTER)
		self.proglabel.grid(row=4, stick=E+W, pady=2)
		
		self.buttonsync = Button(frame, text="Sync Now",  command=self.sync_selected)
		self.buttonsync.grid(row=5, column=0, sticky=W+E)
		#self.buttonlaunch = Button(frame, text="Launch ArmA 2 with these addons", command=self.say_hi)
		#self.buttonlaunch.grid(row=6, column=0, sticky=W+E)
		#######################################################################
		# End GUI stuff
		
		# Set up the initial GUI states
		
		# Disable the buttons if we don't have any server selected
		selected = self.listbox.curselection()
		if len(selected) == 0:
			self.buttonsync['state'] = DISABLED
			#self.buttonlaunch['state'] = DISABLED
		
		# Update the listbox contents to show stuff from config file
		self.update_listbox()
		
		# Start watching the listbox for clicks (to enable buttons)
		self.poll_listbox()
		
	def read_config(self):
		# Read our configuation files
		sync_config = ConfigParser.RawConfigParser()
		server_config = ConfigParser.RawConfigParser()
		
		# Read the TaoSync config
		try:
			with open(os.path.join(os.getcwd(), "taosync.cfg")) as cfg_file:
				sync_config.readfp(cfg_file)
				
			self.addon_directory = sync_config.get("TaoSync","AddonDirectory")
			#self.arma_exe = sync_config.get("TaoSync","ArmAExe")
		except:
			print "WARN: Couldn't read sync config file. Using defaults."
			#raise
			# It's okay, defaults are fine for now.
			
		# Read the server config
		try:
			with open(os.path.join(os.getcwd(), "taosync_serverlist.cfg")) as cfg_file:
				server_config.readfp(cfg_file)
				
			for server in server_config.sections():
				# [mf.address, "file:////" + mf.manifestaddress]
				server_address = server_config.get(server, "Address")
				server_manifest_address = server_config.get(server, "ManifestAddress")
				self.known_servers[server] = [server_address, server_manifest_address]
		except:
			print "WARN: Couldn't read server list file. No servers loaded to list."
			#raise
			
	def write_config(self):
		# Write our configuation files
		sync_config = ConfigParser.RawConfigParser()
		server_config = ConfigParser.RawConfigParser()
		
		# Write the TaoSync config
		try:
			try:
				with open(os.path.join(os.getcwd(), "taosync.cfg")) as cfg_file:
					sync_config.readfp(cfg_file)
			except:
				print "INFO: No sync config exists, making new one."
				
			if not sync_config.has_section("TaoSync"):
				sync_config.add_section("TaoSync")
			sync_config.set("TaoSync","AddonDirectory", self.addon_directory)
			#sync_config.set("TaoSync","ArmAExe", self.arma_exe)
			
			sync_config.write(open(os.path.join(os.getcwd(), "taosync.cfg"), 'wb'))
		except:
			print "WARN: Couldn't write sync config file."
			raise
			
		# Write the server config
		try:
			try:
				with open(os.path.join(os.getcwd(), "taosync_serverlist.cfg")) as cfg_file:
					server_config.readfp(cfg_file)
			except:
				print "INFO: No server config exists, making new one."
			
			print "known svrs", self.known_servers
			if len(self.known_servers) > 0:
				for server in self.known_servers:
					if not server_config.has_section(server):
						server_config.add_section(server)
					server_config.set(server,"Address",self.known_servers[server][0])
					server_config.set(server,"ManifestAddress",self.known_servers[server][1])
				
			server_config.write(open(os.path.join(os.getcwd(), "taosync_serverlist.cfg"), 'wb'))
		except:
			print "WARN: Couldn't write server config file."
			raise
	
	def about(self):
		tkMessageBox.showinfo(title="About TaoSync", message="TaoSync, an addon synchronization program for ArmA 2.\n\nCopyright (C) 2012 Ryan Schultz.\n\n Version %s" % self.version) 
		
	def build_repository(self):
		repo_dir = tkFileDialog.askdirectory(initialdir=self.addon_directory, parent=root,title='Select local repository directory',mustexist=True)
		
		if repo_dir != '' and repo_dir != None:
			try:
				self.block_ui()
				self.progressbar['mode'] = 'indeterminate'
				self.progressbar.start()
				
				save_to = tkFileDialog.asksaveasfilename(parent=root, initialdir=repo_dir,defaultextension=".xml",filetypes=[('All files', '.*'), ('TaoSync XML files', '.xml')],title="Save manifest as")
				if save_to != '' and save_to != None:
					self.proglabel['text'] = "Building repository..."
					name_string = tkSimpleDialog.askstring("Enter repository name", prompt="Enter repository name:")
					address_string = tkSimpleDialog.askstring("Enter repository web address", prompt="Enter repository web address:")
					manifestaddress_string = address_string + os.path.basename(save_to)
					mf = Manifest.Manifest(address = address_string, manifestaddress = manifestaddress_string, servername = name_string)
					mf.make_file_list(repo_dir)
					build_thread = threading.Thread(target=mf.build_manifest, args=(save_to,))
					build_thread.start()
					build_wait_thread = threading.Thread(target=self.wait_to_finish_build, args=(build_thread,))
					build_wait_thread.start()
					#mf.build_manifest(save_to)
					
				self.unblock_ui()
				self.progressbar['mode'] = 'determinate'
				self.progressbar.stop()
			except:
				tkMessageBox.showerror(title="Error", message="Unable to build repository. Check your files and try again.")
				raise
			
			
	
	def wait_to_finish_build(self, build_thread):
		build_thread.join()
		# Will block until sync finishes
		self.progressbar.stop()
		self.proglabel['text'] = "Build finished."
		self.unblock_ui()
			
			
	def update_listbox(self):
		self.listbox.delete(0, END) # Delete all
		for server in self.known_servers:
			self.listbox.insert(END, server)
		self.listbox.select_set(0)
	
	def poll_listbox(self):
		now = self.listbox.curselection()
		if now != self.current_selection:
			self.listbox_changed(now)
			self.current_selection = now
		self.master.after(200, self.poll_listbox)
		
	def listbox_changed(self, selection):
		# Check if something has been selected ever, and if so unblock the UI
		if len(selection) != 0:
			# Update the current server
			self.current_server = self.listbox.get(selection)
			if self.ui_blocked == False:
				# Once something has been selected, there's no going back.
				# So we can simply unblock the whole UI.
				self.unblock_ui()
			
		
	def block_ui(self):
		self.ui_blocked = True
		self.listbox['state'] = DISABLED
		self.buttonsync['state'] = DISABLED
		#self.buttonlaunch['state'] = DISABLED
		
	def unblock_ui(self):
		self.ui_blocked = False
		self.listbox['state'] = NORMAL
		self.buttonsync['state'] = NORMAL
		#self.buttonlaunch['state'] = NORMAL
		
	def select_addon_directory(self):
		addon_dir = tkFileDialog.askdirectory(initialdir=self.addon_directory, parent=root,title='Select location to store addons',mustexist=True)
		
		if addon_dir != '' and addon_dir != None:
			self.addon_directory = addon_dir
			self.write_config()
		
	def add_local_manifest(self):
		local_manifest = tkFileDialog.askopenfilename(parent=root,title='Select manifest file',filetypes=[('All files', '.*'), ('TaoSync XML files', '.xml')])
		#local_manifest = "file://" + dialog_file
		if local_manifest != '' and local_manifest != None:
			self.block_ui()
			self.progressbar['mode'] = 'indeterminate'
			self.progressbar.start()
			
			try:
				mf = Manifest.Manifest(manifestaddress = local_manifest)
				mf.read_manifest(local_manifest)
				
				already_exists = mf.servername in self.known_servers
				self.known_servers[mf.servername] = [mf.address, "file:////" + mf.manifestaddress]
				
				self.unblock_ui()
				if already_exists == False:
					# Just update the existing entry, don't add a duplicate.
					self.listbox.insert(END, mf.servername)
				
				self.write_config()
			except:
				tkMessageBox.showerror(title="Error", message="Unable to open manifest file. It may be malformed or corrupted -- try downloading it again.")
				raise
			
			self.progressbar['mode'] = 'determinate'
			self.progressbar.stop()
	
	def add_net_manifest(self):
		net_manifest = tkSimpleDialog.askstring(title='Enter manifest URL',prompt="Enter URL:")
		
		if net_manifest != '' and net_manifest != None:
			try:
				print "INFO: Downloading manifest %s..." % net_manifest
				#self.block_ui()
				self.progressbar['mode'] = 'indeterminate'
				self.progressbar.start()
				
				local_manifest = urllib.urlretrieve(net_manifest)[0] # Only want the filename, not the headers
				
				try:
					mf = Manifest.Manifest(manifestaddress = local_manifest)
					mf.read_manifest(local_manifest)
					
					already_exists = mf.servername in self.known_servers
					self.known_servers[mf.servername] = [mf.address, "file:////" + mf.manifestaddress]
					
					if already_exists == False:
						# Just update the existing entry, don't add a duplicate.
						self.listbox.insert(END, mf.servername)
						
					self.write_config()
				except:
					tkMessageBox.showerror(title="Error", message="Unable to open manifest file. It may be malformed or corrupted -- try downloading it again.")
					raise
					
				self.progressbar['mode'] = 'determinate'
				self.progressbar.stop()
				
			except:
				tkMessageBox.showerror(title="Error", message="Unable to download manifest. Check your URL and try again.")
				raise
			
	def update_progress_bar(self):
		if self.is_syncing == True and self.ts != None and self.ts.didProgress == True:
			self.progressbar.step(self.ts.progress_increment)
			self.ts.didProgress = False
			
		if self.is_syncing == True:
			self.master.after(1000, self.update_progress_bar)
		
	def wait_to_finish_sync(self, sync_thread):
		sync_thread.join()
		# Will block until sync finishes
		self.ts = None
		self.progressbar.stop()
		self.proglabel['text'] = "Sync finished for %s." % self.current_server
		self.is_syncing = False
		self.unblock_ui()
		
			
	def sync_selected(self):
		try:
			selected = self.listbox.get(self.listbox.curselection())
			
			if selected != (): # If nothing is selected, do nothing
				self.ts = TaoSync.TaoSync(self.known_servers[selected][1]) #1 is the manifest address
				try:
					self.is_syncing = True
					self.progressbar['mode'] = 'determinate'
					#self.progressbar.start()
					self.update_progress_bar()
					self.proglabel['text'] = "Syncing %s..." % self.current_server
					self.buttonsync['state'] = DISABLED
					#self.buttonlaunch['state'] = DISABLED
					sync_thread = threading.Thread(target=self.ts.sync, args=())
					sync_thread.start()
					wait_thread = threading.Thread(target=self.wait_to_finish_sync, args=(sync_thread,))
					wait_thread.start()
					
					
				except:
					tkMessageBox.showerror(title="Error", message="Error while syncing.")
					self.progressbar.stop()
					self.proglabel['text'] = "Sync error!" 
					raise
		except: 
			print "WARN: Nothing selected."
			raise
			
if __name__ == "__main__":
	root = Tk()

	   
	icon_b64 = """R0lGODlhIAAgAKIAABj/AAMgAP8AAAAAAP///wAAAAAAAAAAACH5BAEAAAQALAAAAAAgACAAAAN/
SLrc/jDKKaq9OGumu7/cJwykWIUeqZpop5Ziq70Du4i0reCvTvCr2GRBGxoVxeMwqZQwm5AnlEal
HqvYlzHLPQYCXNVXAiibv+Gvec1Yn8FYtbvcngPQVLm9bseLA3ZmfH1wA3qBg4SGgIGCC41vjJAA
iZOWlZaQUJucnZ4LCQA7"""
	import base64
	icondata = base64.b64decode(icon_b64)
	## The temp file is icon.ico
	tempFile= "icon.gif"
	iconfile= open(tempFile,"wb")
	## Extract the icon
	iconfile.write(icondata)
	iconfile.close()
	   
	ico = PhotoImage(file='icon.gif')
	root.tk.call('wm', 'iconphoto', root._w, ico)
	os.remove(tempFile)
	
	app = TaoSyncGUI(root)

	root.mainloop()